﻿/* *****************************************************************************
ShoutcastRunner 0.3

Copyright (c) KEXP 90.3 FM Seattle. All rights reserved. 
www.kexp.org
author: Louis O'Callaghan (louis@kexp.org)
October 2012

Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0  

THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
MERCHANTABLITY OR NON-INFRINGEMENT. 

See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.

SHOUTcast™ is a trademark of AOL LLC.
***************************************************************************** */

using System;
using System.IO;
using System.ServiceProcess;
using System.Threading;
using log4net;

namespace Kexp.ShoutcastRunner
{
	public partial class ShoutcastRunner : ServiceBase
	{
		private static readonly ILog Log = LogManager.GetLogger(typeof(ShoutcastRunner));

		private const int WaitSleepInterval = 5000;

		private int _dayOfMonthlyRotation;
		private Timer _dailyParseLogTimer;
		private Timer _monthlyRotateLogTimer;
		private ShoutcastProcess _process;
		private CrossdomainServer _server;

		public ShoutcastRunner()
		{
			InitializeComponent();

			ServiceName = "ShoutcastRunner";
			CanStop = true;
			AutoLog = true;
		}
		
		private void RotateLogs(object sender)
		{
			try
			{
				// do this first as the other process should be skipping today
				_process.ParseDailyLogs();
				_process.RotateLogs();
			}
			catch (Exception e)
			{
				Log.Error("Log rotation failed with exception.", e);
			}

			ResetMonthlyLogTimer();
		}

		private void ParseDailyLogs(object sender)
		{
			try
			{
				_process.ParseDailyLogs();
			}
			catch (Exception e)
			{
				Log.Error("Parse daily log failed with exception.", e);
			}

			ResetDailyLogTimer();
		}

		private void ResetDailyLogTimer()
		{
			// some help from here:
			// http://stackoverflow.com/questions/4529019/how-to-use-the-net-timer-class-to-trigger-an-event-at-a-specific-time
			// in hindsight it seems obvious
			if (_dailyParseLogTimer == null)
				_dailyParseLogTimer = new Timer(ParseDailyLogs);

			int msToMidnight = Helpers.GetMillisecondsUntilMidnight();
			var dateOfNextRun = DateTime.Now.AddMilliseconds(msToMidnight);

			// if the next run time is the same as the log rotation run time, skip it
			if (dateOfNextRun.Day == _dayOfMonthlyRotation)
			{
				msToMidnight += 1000*60*60*24;
				Log.Debug("Adding a day to the daily log run interval in deference to the montly log rotation.");
			}

			Log.Debug(string.Format("Reset daily log timer to {0} ms ({1}).", 
				msToMidnight, Helpers.GetTimelineFromMilliseconds(msToMidnight)));
			_dailyParseLogTimer.Change(msToMidnight, Timeout.Infinite);
		}

		private void ResetMonthlyLogTimer()
		{
			if (_monthlyRotateLogTimer == null)
				_monthlyRotateLogTimer = new Timer(RotateLogs);

			long msToNextMonth = Helpers.GetMillisecondsUntilDayOfMonth(_dayOfMonthlyRotation);
			Log.Debug(string.Format("Reset monthly log timer to {0} ms ({1}).", 
				msToNextMonth, Helpers.GetTimelineFromMilliseconds(msToNextMonth)));
			_monthlyRotateLogTimer.Change(msToNextMonth, Timeout.Infinite);
		}

		private void WaitAndRestartShoutcast(object sender, FileSystemEventArgs e)
		{
			Log.Info("New preroll file detected.");

			// wait five seconds first
			Thread.Sleep(WaitSleepInterval);

			_process.Restart();
		}

		protected override void OnStart(string[] args)
		{
			Log.Info("Service starting.");

			_process = new ShoutcastProcess();
			_process.ContentChanged += WaitAndRestartShoutcast;
			_process.MonitorContent = Settings.EnableContentMonitoring;
			_process.Start();

            _dayOfMonthlyRotation = Settings.DayOfMonthlyLogRotation;

			ResetMonthlyLogTimer();
			ResetDailyLogTimer();

            if (Settings.EnableCrossdomainServer)
            {
                _server = new CrossdomainServer();
                _server.Start();
            }
		}

		protected override void OnStop()
		{
			Log.Info("Service stopping.");

			if (_server != null)
				_server.Stop();

			if (_dailyParseLogTimer != null)
				_dailyParseLogTimer.Dispose();

			if (_monthlyRotateLogTimer != null)
				_monthlyRotateLogTimer.Dispose();

			_process.Close();
			Log.Info("Service stopped.");
		}
	}
}
